<?php
/**
 * 防止csrf攻击
 * @author Calvin
 *
 */

class Csrf
{
    protected $ci;
    protected $csrf_controller=array("user","files","trace","register","login","find_pwd");
    protected $csrf_white_list=array("register/index","register/send_email_success","register/active","register/code","login/index","login/logout","login/code","login/run_tryout","login/tryout","find_pwd/index","find_pwd/code","find_pwd/valid_mobile","find_pwd/reset_pwd_by_sms","find_pwd/send_email_success","find_pwd/send_email_error","find_pwd/reset_pwd","find_pwd/reset_success","files/index","files/get_packet_percent","files/export_source_file","trace/index","user/user_info","user/update_pwd","user/update_pwd_success","user/new_email","user/valid_phone","user/new_phone","user/save_new_phone_success","user/valid_phone_success","user/send_valid_email_success","user/send_new_email_success");
    protected $csrf_not_recreate = array("files/get_packet_percent");

    const ACCESS_TOKEN_KEY = "__CSRF__";
    const WHITE_LIST_ACTION_KEY = "__CSRF_WHITE_LIST__";
    /*function __get($key)
    {
        $CI = & get_instance();
        return $CI->$key;
    }*/

    public function __construct()
    {
        $this->ci = &get_instance();
    }
    private function generate($force=FALSE)
    {
        //$cookie_token = $this->input->cookie(self::ACCESS_TOKEN_KEY);
        /*if($reset==FALSE && !empty($cookie_token))
        {
            return TRUE;
        }*/
        $sess_token = $this->ci->session->userdata(self::ACCESS_TOKEN_KEY);
        $cookie_token = $this->ci->input->cookie(self::ACCESS_TOKEN_KEY);
        if(!empty($sess_token) && !empty($cookie_token) && !$force)
        {
            //var_dump("123");
            $this->set_csrf_cookie($sess_token);
            return TRUE;
        }
        $token = Common::uniqid();
        //$token_sess = $this->session->userdata(self::ACCESS_TOKEN_KEY);
        /*if(empty($token_sess))
        {
            $token_sess=array();
        }*/
        //$token_sess[]=$token;
        //$token_sess=$token;
        /*if(!$reset)
        {
            $white_list = $this->session->userdata(self::WHITE_LIST_ACTION_KEY);
            if(empty($white_list))
            {
                $white_list = array();
            }
            $url = $this->get_url();
            if(!in_array($url,$white_list))
            {
                $white_list[]=$url;
            }
            $this->session->set_userdata(self::WHITE_LIST_ACTION_KEY,$white_list);
        }*/
        /*$cookie = array(
            'name'   => self::ACCESS_TOKEN_KEY,
            'value'  => $token,
            'expire' => $this->ci->config->item("no_operation_exp_time","login"),
            'path'   => '/',
            'secure' => FALSE
        );
        $this->ci->input->set_cookie($cookie);*/
        $this->set_csrf_cookie($token);
        return $token;
    }

    private function set_csrf_cookie($token)
    {
        $this->ci->config->load("user");
        $this->ci->session->set_userdata(self::ACCESS_TOKEN_KEY,$token);
        $cookie = array(
            'name'   => self::ACCESS_TOKEN_KEY,
            'value'  => $token,
            'expire' => $this->ci->config->item("no_operation_exp_time","login"),
            'path'   => '/'
        );
        $this->ci->input->set_cookie($cookie);
    }

    /*public function ajax_access_valid()
    {
        if($this->input->is_ajax_request())
        {
            $this->access_valid();
        }
    }*/

    /*private function get_url()
    {
        $controller = $this->router->fetch_class();
        $action = $this->router->fetch_method();
        return $controller."/".$action;
    }*/
    private function valid($header_toten="")
    {
        //return TRUE;
        //$white_list = $this->session->userdata(self::WHITE_LIST_ACTION_KEY);
        //$url = $this->get_url();
        //Common::console()
        /*if(!empty($white_list) && in_array($url,$white_list))
        {
            $this->access_token();
            return TRUE;
        }*/
        //$token_cokie = $this->input->set_cookie(self::ACCESS_TOKEN_KEY);
        $token_sess = $this->ci->session->userdata(self::ACCESS_TOKEN_KEY);
        if(empty($token_sess))
        {
            $this->generate(TRUE);
            Common::ajax_return(1012);
        }
        else
        {
            $header_toten = !empty($header_toten)?$header_toten:$this->ci->input->get_request_header("X-Csrf-Token",TRUE);
            if(empty($header_toten))
            {
                $header_toten = $this->ci->input->get("_csrf_token");
            }
            //$find = array_search($header_toten,$token_sess);
            //var_dump($this->ci->input->get_request_header("X-Csrf-Token",TRUE));
            $find = $header_toten == $token_sess;
            $this->generate();
            if($find===FALSE)
            {
                Common::ajax_return(1012);
            }
            //array_splice($token_sess,$find,1);
            //Common::console($token_sess,true);
            //$this->session->set_userdata(self::ACCESS_TOKEN_KEY,$token_sess);
            //$this->access_token(TRUE);
            return TRUE;
        }
    }


    public function valid_csrf()
    {
        if(!config_item("check_csrf_token"))
        {
            return TRUE;
        }
        if($this->is_restful_api())
        {
            return TRUE;
        }
       // $controller = $this->ci->router->fetch_class();
        //$action = $this->ci->router->fetch_method();
        //$uri = $controller."/".$action;
        $uri = $this->get_url();
        //$this->ci->load->library("csrf_valid");
        if(in_array($uri,$this->csrf_white_list))
        {
            $this->generate();
        }
        else if(in_array($this->ci->router->class,$this->csrf_controller))
        {
            $this->valid();
        }
        else
        {
            $this->generate();
        }
        return TRUE;
    }
    private function is_restful_api()
    {
        return isset($this->ci->uri->segments[1])?(bool)preg_match('/^v[0-9\.]+/',$this->ci->uri->segments[1]):FALSE;
    }
    private function get_url()
    {
        $controller = $this->ci->router->class;
        $action =$this->ci->router->method;
        return $controller."/".$action;
    }
}